package com.xsf.pay.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.xsf.pay.common.constants.BusinessConstants;
import com.xsf.pay.common.domain.CodeMsg;
import com.xsf.pay.common.exception.BusinessRunTimeException;
import com.xsf.pay.common.utils.StringUtil;
import com.xsf.pay.service.RedisService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Description
 *
 * @author 呆毛一哥
 * @date 2023/11/09 15:03
 */
@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    @Resource
    public RedisTemplate<String, Object> redisTemplate;

    @Override
    public Object getObjectFromSessionByKey(HttpServletRequest request, String key) {
        Object obj = null;
        if (request == null) {
            return null;
        }
        String token = request.getHeader(BusinessConstants.ACCESS_TOKEN);
        if (token != null) {
            //开启redis，用户数据放在redis中，从redis中获取
            if (redisTemplate.opsForHash().hasKey(token, key)) {
                //redis中存在，拿出来使用
                obj = redisTemplate.opsForHash().get(token, key);
                redisTemplate.expire(token, BusinessConstants.MAX_SESSION_IN_SECONDS, TimeUnit.SECONDS);
            }
        }
        return obj;
    }

    @Override
    public void storageObjectBySession(String token, String key, Object obj) {
        if(ObjectUtil.isNull(obj)){
            return;
        }
        //开启redis，用户数据放到redis中
        redisTemplate.opsForHash().put(token, key, obj.toString());
        redisTemplate.expire(token, BusinessConstants.MAX_SESSION_IN_SECONDS, TimeUnit.SECONDS);
    }

    @Override
    public void deleteObjectBySession(HttpServletRequest request, String key) {
        if (request != null) {
            String token = request.getHeader(BusinessConstants.ACCESS_TOKEN);
            if (StringUtil.isNotEmpty(token)) {
                //开启redis，用户数据放在redis中，从redis中删除
                redisTemplate.opsForHash().delete(token, key);
            }
        }
    }

    @Override
    public void deleteObjectByUserAndIp(Long userId, String clientIp) {
        Set<String> tokens = redisTemplate.keys("*");
        assert tokens != null;
        for (String token : tokens) {
            Object userIdValue = redisTemplate.opsForHash().get(token, "userId");
            Object clientIpValue = redisTemplate.opsForHash().get(token, "clientIp");
            if (userIdValue != null && clientIpValue != null && userIdValue.equals(userId.toString()) && clientIpValue.equals(clientIp)) {
                redisTemplate.opsForHash().delete(token, "userId");
            }
        }
    }

    @Override
    public Boolean delete(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.delete(key);
    }

    @Override
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }


    @Override
    public Boolean hasKey(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.hasKey(key);
    }

    @Override
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        if (null == key || null == unit) {
            return false;
        }
        return redisTemplate.expire(key, timeout, unit);
    }

    @Override
    public Set<String> keys(String pattern) {
        if (null == pattern) {
            return null;
        }
        return redisTemplate.keys(pattern);
    }

    @Override
    public Boolean persist(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.persist(key);
    }

    @Override
    public Long getExpire(String key, TimeUnit unit) {
        if (null == key || null == unit) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.getExpire(key, unit);
    }

    @Override
    public void set(String key, Object value) {

        if (null == key || null == value) {
            return;
        }
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public void set(String key, Object value, long time, TimeUnit unit) {
        if (null == key || null == value || null == unit) {
            return;
        }
        if (time < 0) {
            redisTemplate.opsForValue().set(key, value);
        } else {
            redisTemplate.opsForValue().set(key, value, time, unit);
        }
    }

    @Override
    public Boolean setifAbsen(String key, Object value, long time, TimeUnit unit) {
        if (null == key || null == value || null == unit) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, unit);
    }

    @Override
    public Object get(String key) {

        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Object getSet(String key, Object value) {

        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    @Override
    public List<Object> mget(Collection<String> keys) {

        if (null == keys) {
            return Collections.emptyList();
        }
        return redisTemplate.opsForValue().multiGet(keys);
    }

    @Override
    public long incrby(String key, long increment) {
        if (null == key) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForValue().increment(key, increment);
    }

    @Override
    public Long decrby(String key, long decrement) {
        if (null == key) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForValue().decrement(key, decrement);
    }

    @Override
    public Integer append(String key, String value) {
        if (key == null) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForValue().append(key, value);
    }

    @Override
    public Object hget(String key, Object field) {
        if (null == key || null == field) {
            return null;
        }
        return redisTemplate.opsForHash().get(key, field);
    }

    @Override
    public void hset(String key, Object field, Object value) {
        if (null == key || null == field) {
            return;
        }
        redisTemplate.opsForHash().put(key, field, value);
    }

    @Override
    public Boolean hexists(String key, Object field) {
        if (null == key || null == field) {
            return false;
        }
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    @Override
    public Long hdel(String key, Object... fields) {
        if (null == key || null == fields || fields.length == 0) {
            return 0L;
        }
        return redisTemplate.opsForHash().delete(key, fields);
    }


    @Override
    public Map<Object, Object> hgetall(String key) {
        if (key == null) {
            return null;
        }
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public void hmset(String key, Map<String, Object> hash) {

        if (null == key || null == hash) {
            return;
        }
        redisTemplate.opsForHash().putAll(key, hash);
    }

    @Override
    public List<Object> hmget(String key, Collection<Object> fields) {
        if (null == key || null == fields) {
            return null;
        }
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    @Override
    public Long hIncrBy(String key, Object field, long increment) {
        if (null == key || null == field) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForHash().increment(key, field, increment);

    }

    @Override
    public Long lpush(String key, Object... strs) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().leftPushAll(key, strs);
    }

    @Override
    public Long rpush(String key, Object... strs) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().rightPushAll(key, strs);
    }

    @Override
    public Object lpop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().leftPop(key);
    }

    @Override
    public Object rpop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().rightPop(key);
    }

    @Override
    public List<Object> lrange(String key, long start, long end) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public void ltrim(String key, long start, long end) {
        if (null == key) {
            return;
        }
        redisTemplate.opsForList().trim(key, start, end);
    }

    @Override
    public Object lindex(String key, long index) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().index(key, index);
    }

    @Override
    public Long llen(String key) {

        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public Long sadd(String key, Object... members) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().add(key, members);

    }

    @Override
    public Long scard(String key) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().size(key);

    }

    @Override
    public Boolean sismember(String key, Object member) {
        if (null == key) {
            return false;
        }
        return redisTemplate.opsForSet().isMember(key, member);

    }

    @Override
    public Object srandmember(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().randomMember(key);

    }

    @Override
    public List<Object> srandmember(String key, int count) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().randomMembers(key, count);

    }

    @Override
    public Object spop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().pop(key);

    }

    @Override
    public Set<Object> smembers(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().members(key);

    }

    @Override
    public Long srem(String key, Object... members) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().remove(key, members);

    }

    @Override
    public Boolean smove(String srckey, String dstkey, Object member) {
        if (null == srckey || null == dstkey) {
            return false;
        }
        return redisTemplate.opsForSet().move(srckey, member, dstkey);

    }


    @Override
    public Set<Object> sUnion(String key, String otherKeys) {
        if (null == key || otherKeys == null) {
            return null;
        }
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    @Override
    public Boolean zadd(String key, double score, Object member) {
        if (null == key) {
            return false;
        }
        return redisTemplate.opsForZSet().add(key, member, score);

    }


    @Override
    public Long zrem(String key, Object... members) {
        if (null == key || null == members) {
            return 0L;
        }
        return redisTemplate.opsForZSet().remove(key, members);

    }

    @Override
    public Long zcard(String key) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForZSet().size(key);
    }

    @Override
    public Double zincrby(String key, double score, Object member) {
        if (null == key) {
            throw new BusinessRunTimeException(CodeMsg.PARAM_CHECK_ERROR);
        }
        return redisTemplate.opsForZSet().incrementScore(key, member, score);
    }

    @Override
    public Long zcount(String key, double min, double max) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForZSet().count(key, min, max);

    }

    @Override
    public Long zrank(String key, Object member) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().rank(key, member);

    }

    @Override
    public Double zscore(String key, Object member) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().score(key, member);
    }

    @Override
    public Set<Object> zrange(String key, long min, long max) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().range(key, min, max);

    }

    @Override
    public Set<Object> zReverseRange(String key, long start, long end) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().reverseRange(key, start, end);

    }

    @Override
    public Set<Object> zrangebyscore(String key, double min, double max) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);

    }


    @Override
    public Set<Object> zrevrangeByScore(String key, double min, double max) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    @Override
    public String createSequencePolicy(String key, String type) {
        String sequence = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        StringBuilder sb = new StringBuilder();
        if (redisTemplate.getConnectionFactory() != null) {
            RedisAtomicLong aLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
            long andIncrement = aLong.getAndIncrement();
            if (andIncrement >= 99999999) {
                redisTemplate.delete(key);
            }
            String id = String.format("%08d", andIncrement);
            sb.append(sequence).append(type).append(id);
        } else {
            throw new RuntimeException("系统异常,请联系客服");
        }
        return sb.toString();
    }

    @Override
    public Long scan(String pattern) {
        if (null == pattern) {
            return null;
        }
        Set<String> set = redisTemplate.keys(pattern);
        assert set != null;
        return redisTemplate.delete(set);
    }

    @Override
    public boolean lock(String key, String value) {
        //如果键不存在则新增,存在则不改变已经有的值
        boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (ifAbsent) {
            return true;
        }
        //解决死锁，且当多个线程同时来时，只会让一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key).toString();
        //如果过期
        if (!StringUtil.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value).toString();
            return !StringUtil.isEmpty(oldValue) && oldValue.equals(currentValue);
        }
        return false;
    }

    @Override
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key).toString();
            if (!StringUtil.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            log.error("【redis锁】解锁失败", e);
        }
    }
}
